﻿using Cronos;
using FastBuild.TimeJob;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System.Data;
using System.Reflection;

namespace FastBuild.Init.TimeJob
{
    /// <summary>
    /// 定时任务服务
    /// </summary>
    public interface ITimeJobService
    {
        /// <summary>
        /// 重启所有任务
        /// </summary>
        void ReStartJob();

        /// <summary>
        /// 重启单个任务
        /// </summary>
        /// <param name="jobName">任务名称</param>
        void ReStartJob(string jobName);

        List<JobInfo> GetJobInfos();
    }

    [Injection(DIPattern.Singleton)]
    internal class TimeJobService : ITimeJobService
    {
        public Dictionary<string, JobExecutSetting> JobExecutSettings { get; set; } = new Dictionary<string, JobExecutSetting>();

        [Autowired]
        public ILog _log;

        private TimeJobInitOption _option = InitClass.Option;

        /// <summary>
        /// 启动
        /// </summary>
        public void Run()
        {
            //初始化
            InitJobExecutJsonSetting();

            if (JobExecutSettings.Keys.Count() <= 0) return;

            //如果不存在配置项中的业务执行类就删除
            Assembly assemblies = Assembly.GetEntryAssembly();
            List<TypeInfo> list = assemblies.DefinedTypes.Where(y => y.GetInterface(nameof(IJob)) is not null).ToList();
            list.ForEach(l =>
            {
                if (!JobExecutSettings.Keys.Contains(l.Name))
                    JobExecutSettings.Remove(l.Name);
                else
                {
                    var oneJobSetting = JobExecutSettings[l.Name];
                    var jobType = l.AsType();
                    oneJobSetting.TaskJob = jobType;
                    oneJobSetting.SkipWhileExecuting = jobType.GetCustomAttribute(typeof(SkipWhileExecutingAttribute)) != null;
                    oneJobSetting.FirstExecute = jobType.GetCustomAttribute(typeof(ExecuteNowAttribute)) != null;
                }
            });

            StartJobs();
        }

        /// <summary>
        /// 重启所有任务
        /// </summary>
        /// <exception cref="Exception"></exception>
        public void ReStartJob()
        {
            foreach (var item in JobExecutSettings)
            {
                item.Value.IsStop = false;
                item.Value.ErrorCount = 0;
            }
        }

        /// <summary>
        /// 重启指定任务
        /// </summary>
        /// <param name="jobName"></param>
        /// <exception cref="Exception"></exception>
        public void ReStartJob(string jobName)
        {
            if (!JobExecutSettings.ContainsKey(jobName))
                throw new Exception($"Job:{jobName} 不存在");

            // 重启指定任务
            var job = JobExecutSettings.Where(s => s.Key == jobName).First();
            job.Value.IsStop = false;
            job.Value.ErrorCount = 0;
        }

        /// <summary>
        /// 获取任务信息
        /// </summary>
        /// <returns></returns>
        public List<JobInfo> GetJobInfos()
        {
            return JobExecutSettings.Select(s => new JobInfo()
            {
                JobName = s.Key,
                ErrorCount = s.Value.ErrorCount,
                IsStop = s.Value.IsStop,
                TaskJob = s.Value.TaskJob
            }).ToList();
        }

        /// <summary>
        /// 运行定时器
        /// </summary>
        private void StartJobs()
        {
            foreach (var item in JobExecutSettings)
            {
                var oneJob = item.Value;
                oneJob.NextExecuteTime = GetNextTime(oneJob.CronString);
                oneJob.Timer = new Timer(s => ExecuteAsync(item.Key), null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1));
            }
        }

        /// <summary>
        /// 执行任务
        /// </summary>
        /// <param name="jobName"></param>
        private void ExecuteAsync(string jobName)
        {
            var jobSetting = JobExecutSettings[jobName];

            // 如果停止了就不执行
            if (jobSetting.IsStop) return;

            if (jobSetting.FirstExecute ||
                // 只精确到秒
                (jobSetting.NextExecuteTime.HasValue && jobSetting.NextExecuteTime.Value == DateTime.Parse($"{DateTime.Now:yyyy-MM-dd HH:mm:ss}")))
            {
                Task.Run(async () =>
                {
                    if (!(jobSetting.SkipWhileExecuting && jobSetting.IsExecuting))
                    {
                        try
                        {
                            jobSetting.IsExecuting = true;

                            var serviceProvider = ServiceContainer.GetScopeServiceProvider();

                            var logScope = serviceProvider.GetService<LogScope>();
                            logScope.LogScopeName = jobName;
                            var service = serviceProvider.GetServiceWithAutowired(jobSetting.TaskJob) as IJob;

                            try
                            {
                                await service.Execute();
                                jobSetting.ErrorCount = 0;
                                jobSetting.IsStop = false;
                            }
                            catch (Exception ex)
                            {
#if RELEASE
                                _log.Error(ex, $"任务执行失败", jobName);
                                jobSetting.ErrorCount++;
                                if (jobSetting.ErrorCount >= _option.MaxErrorCount)
                                {
                                    jobSetting.IsStop = true;
                                    _log.Error($"{jobName} 超过最大错误次数，已停止执行，错误详情请查看对应任务日志", "TimeJob");
                                }

                                // 自定义错误处理
                                await _option.ErrorHandler(serviceProvider, jobName, jobSetting.ErrorCount, jobSetting.IsStop);
#endif
                            }

                            jobSetting.IsExecuting = false;
                        }
                        catch (Exception ex)
                        {
                            _log.Error(ex, "An error occurred", "TimeJob");
                        }
                    }
                });
                jobSetting.FirstExecute = false;
            }
            jobSetting.NextExecuteTime = GetNextTime(jobSetting.CronString);
        }

        /// <summary>
        /// 初始化JsonSetting
        /// </summary>
        private void InitJobExecutJsonSetting()
        {
            var settingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _option.JobSettingPath);
            if (!File.Exists(settingPath))
                return;

            var jsonString = File.ReadAllText(settingPath);

            var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);

            var returnData = new Dictionary<string, JobExecutSetting>();

            if (data == null)
                return;

            foreach (var item in data)
            {
                if (item.Value is null) continue;

                returnData.Add(item.Key, new JobExecutSetting()
                {
                    CronString = item.Value
                });
            }

            JobExecutSettings = returnData;
        }

        /// <summary>
        /// 根据cron表达式获取下一个执行时间
        /// </summary>
        /// <param name="cron"></param>
        /// <returns></returns>
        private DateTime? GetNextTime(string cron)
        {
            var expression = CronExpression.Parse(cron, CronFormat.IncludeSeconds);

            // 获取下一个执行时间
            DateTimeOffset? next = expression.GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Local);

            return next?.DateTime;
        }
    }

    /// <summary>
    /// Job运行配置
    /// </summary>
    internal class JobExecutSetting
    {
        /// <summary>
        /// Cron表达式
        /// </summary>
        public string CronString { get; set; }

        /// <summary>
        /// 下次执行时间
        /// </summary>
        public DateTime? NextExecuteTime { get; set; }

        /// <summary>
        /// 正在运行是否跳过
        /// </summary>
        public bool SkipWhileExecuting { get; set; }

        /// <summary>
        /// 任务执行类
        /// </summary>
        public Type TaskJob { get; set; }

        /// <summary>
        /// 时间触发器
        /// </summary>
        public Timer Timer { get; set; }

        /// <summary>
        /// 是否在执行中
        /// </summary>
        public bool IsExecuting { get; set; }

        /// <summary>
        /// 刚进入是否执行
        /// </summary>
        public bool FirstExecute { get; set; }

        /// <summary>
        /// 错误次数
        /// </summary>
        public int ErrorCount { get; set; }

        /// <summary>
        /// 是否停止
        /// </summary>
        public bool IsStop { get; set; }
    }

    public class JobInfo
    {
        /// <summary>
        /// 任务名称
        /// </summary>
        public string JobName { get; set; }

        /// <summary>
        /// 错误次数
        /// </summary>
        public int ErrorCount { get; set; }

        /// <summary>
        /// 是否停止
        /// </summary>
        public bool IsStop { get; set; }

        /// <summary>
        /// 任务执行类
        /// </summary>
        public Type TaskJob { get; set; }
    }
}
